import 'package:kq_flutter_core_widget/utils/ex/kq_ex.dart';
import 'package:kq_flutter_widgets/widgets/listener/object_watcher/watchable_object.dart';
import 'package:kq_flutter_widgets/widgets/listener/object_watcher/watcher_callback.dart';

///对象观察者
class Watcher {
  Watcher._internal();

  factory Watcher() => _instance;
  static final Watcher _instance = Watcher._internal();

  final Map<double, WatcherCallback> _objectWatchers = {};
  final List<WatchableObject> _objects = [];
  final Map<double, bool> _bindObjects = {};

  ///绑定对象，
  ///[objects]绑定的一组对象。
  void bindObject(List<WatchableObject> objects) {
    clear();
    _objects.addAll(objects);
    for (WatchableObject str in objects) {
      _bindObjects.putIfAbsent(str.getUuid(), () => _checkObject(str.value()));
    }
  }

  ///检查绑定的对象是否已全部赋值，
  ///[callback]每次赋值都会回调，
  ///[allCheck]是否全部已赋值，是则返回true，不是则返回false。
  void check<T>(Function(bool allCheck) callback) {
    for (WatchableObject str in _objects) {
      str.watcher(
        WatcherCallback(
          (object) {
            _bindObjects.update(
              str.getUuid(),
              (value) => _checkObject(object),
            );
            _realCheck(callback);
          },
        ),
      );
    }
    _realCheck(callback);
  }

  bool _checkObject<T>(T object) {
    return object is String ? object.isNotNullOrEmpty : object != null;
  }

  void _realCheck(Function(bool allCheck) callback) {
    bool isAllCheck = true;
    _bindObjects.forEach((key, value) {
      if (!value) {
        isAllCheck = false;
        return;
      }
    });
    callback.call(isAllCheck);
  }

  ///清除内存。
  void clear() {
    _objects.clear();
    _bindObjects.clear();
    _objectWatchers.clear();
  }

  ///绑定回调执行，
  ///需要[WatchableObject]对象的[uuid]做为键值获取对象绑定。
  T? value<T>(double uuid, T? other) {
    _objectWatchers[uuid]?.onChanged(other);
    return other;
  }

  ///绑定设置，
  ///需要[WatchableObject]对象的[uuid]做为键值，
  ///[watcherCallback]绑定的回调。
  void watcher(double uuid, WatcherCallback watcherCallback) {
    _objectWatchers.putIfAbsent(uuid, () => watcherCallback);
  }
}
